package dao

import (
	"activity_srv/model"
	"errors"
	"fmt"
	"go.uber.org/zap"
	"gorm.io/gorm"
	"math"
	"math/rand"
	"smart-health-platforms/config"
	"smart-health-platforms/pkg"
	"time"
)

// 用户选择商品进行砍价
func BargainAdd(bargainId int, userId int) (string, error) {
	var bar model.Bargain
	bar.Id = uint32(bargainId)
	err := bar.GetBargain()
	if err != nil {
		pkg.Error("查找失败", zap.Error(err))
		return "", err
	}
	code := Generate6DigitInviteCode()
	var bargain model.BargainActivity
	bargain = model.BargainActivity{
		BargainId: int32(bargainId),
		UserId:    int32(userId),
		People:    0,
		Price:     bar.Price,
		Status:    1,
		Code:      code,
		StrTime:   time.Now(),
		EndTime:   time.Now().Add(24 * time.Hour),
		SkrId:     bar.SkuId,
	}
	err = bargain.AddBargainActivity()
	if err != nil {
		pkg.Error("参加失败", zap.Error(err))
		return "", err
	}
	return code, nil
}

const inviteCodeChars = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"

// Generate6DigitInviteCode 生成6位砍价邀请码
func Generate6DigitInviteCode() string {
	rand.Seed(time.Now().UnixNano())
	// 固定生成6位邀请码
	codeLength := 6
	buffer := make([]byte, codeLength)
	for i := 0; i < codeLength; i++ {
		// 从字符集中随机选择一个字符
		buffer[i] = inviteCodeChars[rand.Intn(len(inviteCodeChars))]
	}
	return string(buffer)
}

/*
计算当前砍价进度（已砍金额/总金额)，作为阶段划分依据
实现分段随机金额逻辑：
第一阶段(0%~60%)：生成T的2%~5%随机金
第二阶段(61%~95%)：生成T的0.5%~2%随机金额
第三阶段(96%~100%)：生成T的0.1%~0.5%随机金额
每日帮砍次数限制（最多 3 次）
*/
// 帮砍方法
func BargainHelp(code string, userID int32) (float32, error) {
	// 1. 查询砍价活动（通过邀请码）
	var activity model.BargainActivity
	activity.Code = code
	err := activity.GetCode()
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			pkg.Error("邀请码无效", zap.Error(err))
			return 0, errors.New("邀请码无效")
		}
		pkg.Error("查询活动失败", zap.Error(err))
		return 0, errors.New("查询活动失败")
	}
	// 检查活动时间是否过期
	if time.Now().After(activity.EndTime) {
		pkg.Error("活动已过期")
		return 0, errors.New("活动已过期")
	}
	// 2. 校验：本人不能帮砍
	if activity.UserId == userID {
		pkg.Error("本人不能帮砍")
		return 0, errors.New("本人不能帮砍")
	}
	// 3. 校验：是否已帮砍过（查询bargain_help表）
	var help model.BargainHelp
	err = config.DB.Where("bargain_activity_id = ? AND user_id = ?", activity.Id, userID).First(&help).Error
	if err == nil {
		pkg.Error("用户已帮砍过该活动",
			zap.Int32("userID", userID),
			zap.Int32("activityID", int32(activity.Id)))
		return 0, errors.New("已存在帮砍记录")
	} else if !errors.Is(err, gorm.ErrRecordNotFound) {
		pkg.Error("查询帮砍记录失败",
			zap.Error(err),
			zap.Int32("userID", userID),
			zap.Int32("activityID", int32(activity.Id)))
		return 0, fmt.Errorf("查询帮砍记录失败: %w", err)
	}
	// 规则1：每日帮砍次数限制（最多3次）
	todayStart := time.Now().Truncate(24 * time.Hour) // 今天0点
	var dailyHelpCount int64
	err = config.DB.Model(&model.BargainHelp{}).
		Where("user_id = ? AND created_at >= ?", userID, todayStart).
		Count(&dailyHelpCount).Error
	if err != nil {
		pkg.Error("查询每日帮砍次数失败", zap.Error(err))
		return 0, fmt.Errorf("系统异常，请稍后再试")
	}
	if dailyHelpCount >= 3 {
		pkg.Error("每日帮砍次数已达上限", zap.Int32("userID", userID))
		return 0, errors.New("今日帮砍次数已达上限，请明天再来")
	}
	// 4. 核心：按规则计算砍价金额
	var ba model.Bargain
	ba.Id = uint32(activity.BargainId)
	fmt.Println(activity.Id)
	if err = ba.GetBargain(); err != nil {
		pkg.Error("查询商品信息失败", zap.Error(err))
		return 0, fmt.Errorf("查询商品信息失败: %w", err)
	}
	// 提前校验价格参数
	P := ba.Price               // 产品原价
	B := float64(ba.FinalPrice) // 产品底价
	fmt.Println(P, B)
	// 新增：校验价格参数有效性
	if P <= B {
		pkg.Error("砍价参数异常，原价必须大于底价",
			zap.Float64("price", P),
			zap.Float64("final_price", B))
		return 0, errors.New("活动参数异常")
	}
	remaining := activity.Price - B
	if remaining <= 0 {
		ba.Status = 4
		if err = ba.UpdateBargain(); err != nil {
			pkg.Error("更新活动状态失败", zap.Error(err))
		}
		return 0, errors.New("砍价金额已达到")
	}
	// 生成基础砍价金额（按阶段规则）
	var baseCut float64
	// 新增：根据帮砍人数计算砍价金额
	if ba.People > 0 {
		// 按人数平均分配剩余金额
		remainingHelpers := int32(ba.People) - activity.People
		if remainingHelpers > 0 {
			// 平均每人砍价金额
			averageCut := remaining / float64(remainingHelpers)
			// 加入随机因素（80%-120%的平均值）
			randomFactor := 0.8 + rand.Float64()*0.4
			baseCut = averageCut * randomFactor
			pkg.Info("按人数分配砍价金额",
				zap.Float64("averageCut", averageCut),
				zap.Float64("randomFactor", randomFactor),
				zap.Float64("baseCut", baseCut))
		}
	}
	// 保底金额：不低于0.01元
	if baseCut < 0.01 {
		baseCut = 0.01
	}
	// 最终金额：不超过剩余金额（避免砍过头）
	cutPrice := math.Min(baseCut, remaining)
	// 保留两位小数
	cutPrice = math.Round(cutPrice*100) / 100
	// 5. 开启数据库事务
	tx := config.DB.Begin()
	if tx.Error != nil {
		pkg.Error("开启事务失败", zap.Error(tx.Error))
		return 0, fmt.Errorf("开启事务失败: %w", tx.Error)
	}
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
			panic(r)
		}
	}()
	// 6. 更新砍价活动信息
	activity.Price -= cutPrice
	activity.People += 1
	// 检查是否砍至底价
	if activity.Price <= B {
		activity.Price = B // 确保不低于底价
		ba.Status = 4      // 标记活动完成
		if err := tx.Model(&ba).Update("status", 4).Error; err != nil {
			tx.Rollback()
			pkg.Error("更新活动完成状态失败", zap.Error(err))
			return 0, fmt.Errorf("更新活动状态失败: %w", err)
		}
	}
	if err := tx.Model(&activity).Updates(&activity).Error; err != nil {
		tx.Rollback()
		pkg.Error("更新砍价信息失败", zap.Error(err))
		return 0, fmt.Errorf("更新砍价信息失败: %w", err)
	}
	// 7. 记录帮砍行为
	helpRecord := model.BargainHelp{
		BargainActivityId: int32(activity.Id),
		UserId:            userID,
		Price:             cutPrice,
		Status:            1,
		CreatedAt:         time.Now(),
	}
	if err := tx.Create(&helpRecord).Error; err != nil {
		tx.Rollback()
		pkg.Error("记录帮砍行为失败", zap.Error(err))
		return 0, fmt.Errorf("记录帮砍行为失败: %w", err)
	}
	// 8. 提交事务
	if err := tx.Commit().Error; err != nil {
		pkg.Error("事务提交失败", zap.Error(err))
		return 0, fmt.Errorf("事务提交失败: %w", err)
	}
	return float32(cutPrice), nil
}

// 工具函数：生成[min, max]范围内的随机数
func randomInRange(min, max float64) float64 {
	rand.Seed(time.Now().UnixNano())
	return min + rand.Float64()*(max-min)
}
